import io

type reader<T:io.reader>:io.reader = struct{
    T rd
    [u8] buf = vec_new<u8>(0, 4096)
    int r
    int w
    bool eof
}

fn reader<T:io.reader>.size():int {
    return self.buf.len()
}

fn reader<T:io.reader>.reset(T rd) {
    self.rd = rd
    if self.buf.len() == 0 {
        self.buf = vec_new<u8>(0, 4096)
    }
    self.r = 0
    self.w = 0
    self.eof = false
}

#local
fn reader<T:io.reader>.fill():void! {
    if self.r > 0 {
        // Copy unread data to the buffer start position
        self.buf.copy(self.buf.slice(self.r, self.w))
        self.w -= self.r
        self.r = 0
    }

    if self.w >= self.buf.len() {
        panic('tried to fill full buffer')
    }

    // Read new data: try a limited number of times
    int max_empty_reads = 100
    for int i = max_empty_reads; i > 0; i -= 1 {
        // maybe eof
        int n = self.rd.read(self.buf.slice(self.w, self.buf.len())) catch e {
            if e.msg() == 'EOF' {
                self.eof = true
            }

            throw e
        }
        if n < 0 {
            throw errorf('negative read count')
        }
        self.w += n
        if n > 0 {
            return
        }
        // if n == 0, will try again
    }

    throw errorf('no progress')
}

fn reader<T:io.reader>.read([u8] buf):int! {
    if buf.len() == 0 {
        if self.buffered() > 0 {
            return 0
        }

        self.eof = true
        throw errorf('EOF')
    }

    // buffer is empty, need to read from underlying reader
    if self.r == self.w {
        // if request buffer size larger than internal buffer
        // read directly to avoid copy
        if buf.len() >= self.buf.len() {
            return self.rd.read(buf)
        }

        // fill internal buffer
        self.r = 0
        self.w = 0
        int n = self.rd.read(self.buf)
        if n < 0 {
            panic('negative read count')
        }

        if n == 0 {
            self.eof = true
            throw errorf('EOF')
        }
        self.w += n
    }

    // copy data from internal buffer
    int n = buf.copy(self.buf.slice(self.r, self.w))
    self.r += n
    return n
}

fn reader<T:io.reader>.buffered():int {
    return self.w - self.r
}

fn reader<T:io.reader>.read_until(u8 delim):[u8] {
    [u8] result = vec_new<u8>(0, 0)
    var has_error = false

    for true {
        int i = -1
        for int j = self.r; j < self.w; j += 1 {
            if self.buf[j] == delim {
                i = j - self.r
                break
            }
        }

        // found delim
        if i >= 0 {
            result = self.buf.slice(self.r, self.r + i + 1)
            self.r += i + 1 // move read pointer
            return result
        }

        if has_error {
            result = self.buf.slice(self.r, self.w)
            self.r = self.w
            return result
        }

        // buf is already full of data, without self.r for read consumption
        if self.buffered() >= self.buf.len() {
            result = self.buf
            self.r = self.w
            // throw errorf('buffer full')
            return result
        }

        // fill may read to the end of the file to generate an eof error
        // fill will make 100 attempts until the data is filled successfully, otherwise it will throw no progress
        self.fill() catch e {
            has_error = true
        }
    }

    return result
}

fn reader<T:io.reader>.read_exact([u8] buf):void! {
    int total = 0
    for total < buf.len() {
        int n = self.read(buf.slice(total, buf.len()))
        total += n
    }
}

fn reader<T:io.reader>.read_byte():u8! {
    if self.r == self.w {
        self.fill()
    }

    u8 b = self.buf[self.r]
    self.r += 1
    return b
}

fn reader<T:io.reader>.read_line():string! {
    if self.eof {
        throw errorf('EOF')
    }

    var bytes = self.read_until('\n'.char())

    // 处理 \r\n
    if bytes.len() > 0 && bytes[bytes.len() - 1] == '\n'.char() {
        bytes = bytes.slice(0, bytes.len() - 1)
        if bytes.len() > 0 && bytes[bytes.len() - 1] == '\r'.char() {
            bytes = bytes.slice(0, bytes.len() - 1)
        }
    }

    return bytes as string
}

type writer<T:io.writer>:io.writer = struct{
    T wr
    [u8] buf = vec_new<u8>(0, 4096)
    int n
    bool err
}

fn writer<T:io.writer>.size():int {
    return self.buf.len()
}

fn writer<T:io.writer>.reset(T wr) {
    self.wr = wr
    if self.buf.len() == 0 {
        self.buf = vec_new<u8>(0, 4096)
    }
    self.n = 0
    self.err = false
}

fn writer<T:io.writer>.available():int {
    return self.buf.len() - self.n
}

fn writer<T:io.writer>.buffered():int {
    return self.n
}

#local
fn writer<T:io.writer>.flush():void! {
    if self.err {
        throw errorf('previous write error')
    }
    
    if self.n == 0 {
        return
    }

    int written = 0
    for written < self.n {
        int n = self.wr.write(self.buf.slice(written, self.n)) catch e {
            self.err = true
            throw e
        }
        
        if n < 0 {
            self.err = true
            throw errorf('negative write count')
        }
        
        written += n
    }
    
    self.n = 0
}

fn writer<T:io.writer>.write([u8] buf):int! {
    if self.err {
        throw errorf('previous write error')
    }

    int total_written = 0
    
    for total_written < buf.len() {
        int remaining = buf.len() - total_written
        int available = self.available()
        
        // If buffer is full, flush it first
        if available == 0 {
            self.flush()
            available = self.available()
        }
        
        // If data is larger than buffer, write directly
        if remaining >= self.buf.len() && self.n == 0 {
            int n = self.wr.write(buf.slice(total_written, buf.len())) catch e {
                self.err = true
                throw e
            }
            
            if n < 0 {
                self.err = true
                throw errorf('negative write count')
            }
            
            total_written += n
            continue
        }
        
        // Copy to buffer
        int to_copy = remaining
        if to_copy > available {
            to_copy = available
        }
        
        self.buf.slice(self.n, self.n + to_copy).copy(buf.slice(total_written, total_written + to_copy))
        self.n += to_copy
        total_written += to_copy
    }
    
    return total_written
}

fn writer<T:io.writer>.write_byte(u8 b):void! {
    if self.err {
        throw errorf('previous write error')
    }
    
    if self.available() == 0 {
        self.flush()
    }
    
    self.buf[self.n] = b
    self.n += 1
}
